package com.wjk.kylin.mall.auth.controller;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wjk.kylin.mall.common.core.constant.Constants;
import com.wjk.kylin.mall.common.core.dto.base.BaseOutDTO;
import com.wjk.kylin.mall.common.core.dto.base.DataOutDTO;
import com.wjk.kylin.mall.common.core.exception.BaseException;
import com.wjk.kylin.mall.common.redis.utils.RedisUtil;
import com.wjk.kylin.mall.common.web.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证中心
 */
@Slf4j
@RestController
@RequestMapping("/oauth")
public class AuthController {
    //    @Autowired
//    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private TokenEndpoint tokenEndpoint;


    //    @ApiOperation(value = "OAuth2认证", notes = "login")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "grant_type", defaultValue = "password", value = "授权模式", required = true),
//            @ApiImplicitParam(name = "client_id", defaultValue = "client", value = "Oauth2客户端ID", required = true),
//            @ApiImplicitParam(name = "client_secret", defaultValue = "123456", value = "Oauth2客户端秘钥", required = true),
//            @ApiImplicitParam(name = "refresh_token", value = "刷新token"),
//            @ApiImplicitParam(name = "username", defaultValue = "admin", value = "登录用户名"),
//            @ApiImplicitParam(name = "password", defaultValue = "123456", value = "登录密码"),
//    })
    @PostMapping("/token")
    public BaseOutDTO postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
//        OAuth2AccessToken oAuth2AccessToken;
//        /**
//         * 获取登录认证的客户端ID
//         *
//         * 兼容两种方式获取Oauth2客户端信息（client_id、client_secret）
//         * 方式一：client_id、client_secret放在请求路径中
//         * 方式二：放在请求头（Request Headers）中的Authorization字段，且经过加密，例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
//         */
//        String clientId = RequestUtils.getAuthClientId();
//        switch (clientId) {
////            case AuthConstants.WEAPP_CLIENT_ID:  // 微信认证
////                oAuth2AccessToken = weAppAuthServiceImpl.login(principal, parameters);
////                break;
//            default:
//                oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
//                break;
//        }
//        return oAuth2AccessToken; tokenEnhancer


//        String clientId = parameters.get(Constants.CLIENT_ID_KEY);
//        if(Constants.ADMIN_CLIENT_ID.equals(clientId)){
//            String uuid = parameters.get("uuid");
//            String code = parameters.get("code");
//            String verifyKey = Constants.ADMIN_CAPTCHA_CODE_KEY + uuid;
//            String captcha = (String) redisUtil.get(verifyKey);
//
//            //删除验证码 redis
//            redisUtil.del(verifyKey);
//
//            if (StringUtils.isBlank(captcha)) {
//                throw new BaseException("验证码失效，请重新获取");
//            } else if (!StringUtils.equalsIgnoreCase(captcha, code)) {
//                throw new BaseException("验证码错误，请重新输入");
//            }
//        }
        String clientId = RequestUtil.getOAuth2ClientId();
        log.info("OAuth认证授权 客户端ID:{}，请求参数：{}", clientId, JSONUtil.toJsonStr(parameters));

        OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();

        return DataOutDTO.builder().data(oAuth2AccessToken).build();
    }

    /**
     * 关于/oauth/logout退出登录的端点的说明
     * 因为JWT本身就是字包含的加密文本,所以不需要在服务端存储Token的过期时间,
     * JWT本身就可以验证自己是否正确,以及什么时候过期,所以意味着Token一旦颁发,
     * 从Token本身来说必须等Token本身过期才会失效,为了防止用户退出登录后,
     * Token依旧有效,我们可以在用户退出或者修改密码后将Token加入到Redis中,
     * 并设置过期时间,可以理解为将Token加入黑名单,
     * 再在gateway上添加过滤器识别token是否在黑名单中即可实现用户的退出改密作废Token的功能
     *
     * @return
     */
    @DeleteMapping("/logout")
    public BaseOutDTO logout(HttpServletRequest request) {
        String payload = request.getHeader(Constants.JWT_PAYLOAD_KEY);
        JSONObject jsonObject = JSONUtil.parseObj(payload);
        // JWT唯一标识
        String jti = jsonObject.getStr(Constants.JWT_JTI);
        // JWT过期时间戳(单位:秒)
//        long exp = jsonObject.getLong(Constants.JWT_EXP);
//        long currentTimeSeconds = System.currentTimeMillis() / 1000;
//        // token已过期
//        if (exp > currentTimeSeconds) {
//            redisUtil.set(Constants.TOKEN_BLACKLIST_PREFIX + jti, null, (exp - currentTimeSeconds));
//        }
        Long expireTime = jsonObject.getLong(Constants.JWT_EXP); // JWT过期时间戳(单位：秒)
        if (expireTime != null) {
            long currentTime = System.currentTimeMillis() / 1000;// 当前时间（单位：秒）
            if (expireTime > currentTime) { // token未过期，添加至缓存作为黑名单限制访问，缓存时间为token过期剩余时间
                redisUtil.set(Constants.TOKEN_BLACKLIST_PREFIX + jti, 1, (expireTime - currentTime));
            }
        } else { // token 永不过期则永久加入黑名单
            redisUtil.set(Constants.TOKEN_BLACKLIST_PREFIX + jti, 1);
        }
        return BaseOutDTO.success();
    }
}
